package com.doraro.platform.config.shiro;


import com.doraro.platform.common.utils.Constant;
import com.doraro.platform.common.utils.JwtUtil;
import com.doraro.platform.module.user.model.entity.SysRole;
import com.doraro.platform.module.user.model.entity.SysUser;
import com.doraro.platform.module.user.service.ISysRoleService;
import com.doraro.platform.module.user.service.ISysUserService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.pam.UnsupportedTokenException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.crazycake.shiro.RedisCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

@Component
public class OauthRealm extends AuthorizingRealm {
    private final ISysUserService userService;
    private final ISysRoleService roleService;
    @Autowired
    @Lazy
    public OauthRealm(ISysUserService userService, ISysRoleService roleService, RedisCacheManager redisCacheManager) {
        this.userService = userService;
        this.roleService = roleService;
        super.setAuthenticationCachingEnabled(true);
        super.setCacheManager(redisCacheManager);
    }

    /**
     * 清除认证缓存
     */
    public void clearCachedAuth(PrincipalCollection principals) {
        this.clearCachedAuthorizationInfo(principals);
    }

    @Override
    protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
        final SysUser sysUser = (SysUser) principals.getPrimaryPrincipal();
        return sysUser.getId();
    }

    /**
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        final SysUser sysUser = (SysUser) principals.getPrimaryPrincipal();

        final SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        final List<SysRole> userRoles = roleService.getRolesByUserId(sysUser.getId());
        final HashSet<String> roleNames = new HashSet<>();
        final ArrayList<Integer> ids = new ArrayList<>();
        for (SysRole userRole : userRoles) {
            if (userRole == null) {
                continue;
            }
            final String name = userRole.getName();
            final Integer id = userRole.getId();
            if (!StringUtils.isBlank(name)) {
                roleNames.add(name);
            }
            if (id != null) {
                ids.add(id);
            }
        }
        info.addRoles(roleNames);
        info.addStringPermissions(roleService.getPemsByRoleIds(ids));
        return info;
    }

    /***
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String tokenCredentials = (String) token.getCredentials();
        final String userId = JwtUtil.getUserId(tokenCredentials);
        if (!JwtUtil.verify(tokenCredentials, userId)) {
            throw new UnsupportedTokenException("token不存在或者已经失效");
        }
        final SysUser sysUser = userService.getById(userId);
        if (sysUser == null) {
            throw new UnknownAccountException("无此账号");
        }
        if (0 == sysUser.getUserStatus()) {
            throw new DisabledAccountException("账号被禁用");
        }
        return new SimpleAuthenticationInfo(sysUser, tokenCredentials, getName());
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof Oauth2Token;
    }

    @Override
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        return hasRole(principals, Constant.ADMIN_NAME) || super.isPermitted(principals, permission);
    }
}
